package com.empire.module.member.service.warninggather;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.empire.module.member.controller.admin.warninggather.vo.ActiveGather;
import com.empire.module.member.controller.admin.warninggather.vo.LocationPointVO;
import com.empire.module.member.dal.dataobject.mapconfig.MapConfigDO;
import com.empire.module.member.dal.dataobject.warninggather.WarningGatherDO;
import com.empire.module.member.dal.dataobject.warninggatherperson.WarningGatherPersonDO;
import com.empire.module.member.dal.mysql.warninggather.WarningGatherMapper;
import com.empire.module.member.dal.mysql.warninggatherperson.WarningGatherPersonMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 聚集人员保存服务（抽离原saveOrUpdateGather相关逻辑）
 */
@Service
public class GatherPersonSaveService {
    private static final Logger log = LoggerFactory.getLogger(GatherPersonSaveService.class);

    // 复用原日期格式化器
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Resource
    private WarningGatherMapper warningGatherMapper;
    @Resource
    private WarningGatherPersonMapper warningGatherPersonMapper;

    /**
     * 保存或更新聚集（原saveOrUpdateGather核心逻辑）
     * 带事务保证：聚集主表、人员表操作、旧聚集清理原子性
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdateGather(ActiveGather gather, MapConfigDO configDo) {
        // 入参校验：无配置且无聚集ID时直接返回
        if (configDo == null && gather.getId() == null) {
            log.warn("聚集保存：无配置且无聚集ID，跳过操作");
            return;
        }

        List<LocationPointVO> points = gather.getPoints();
        if (points.isEmpty()) {
            log.warn("聚集保存：聚集无人员点数据，跳过操作");
            return;
        }

        // 1. 统计最频繁的地址（作为聚集地址）
        String mostFrequentAddress = getMostFrequentAddress(points);

        // 2. 处理时间范围（最早时间为开始时间，最晚为结束时间）
        List<LocalDateTime> sortedTimes = points.stream()
                .map(point -> LocalDateTime.parse(point.getLocateTime(), DATE_TIME_FORMATTER))
                .sorted()
                .toList();
        LocalDateTime startTime = sortedTimes.getFirst();
        LocalDateTime endTime = sortedTimes.getLast();

        // 3. 统计参与人员（去重）
        Set<Long> personIds = points.stream()
                .map(LocationPointVO::getPersonId)
                .collect(Collectors.toSet());

        // 4. 保存/更新聚集主表 + 人员表
        Long currentGatherId = saveOrUpdateGatherMain(gather, configDo, startTime, endTime, personIds, mostFrequentAddress);

        // 5. 保存聚集人员（保留阅读标记）
        assert configDo != null;
        saveGatherPersons(currentGatherId, points, configDo.getId());

        // 6. 清理被当前聚集涵盖的旧聚集
        cleanupCoveredOldGathers(currentGatherId, configDo.getId(), startTime, endTime, points);
    }

    /**
     * 保存或更新聚集主表（WarningGatherDO）
     */
    private Long saveOrUpdateGatherMain(ActiveGather gather, MapConfigDO configDo,
                                        LocalDateTime startTime, LocalDateTime endTime,
                                        Set<Long> personIds, String mostFrequentAddress) {
        Long currentGatherId;
        // 新聚集（无ID）：插入主表
        if (gather.getId() == null) {
            WarningGatherDO warningGather = new WarningGatherDO();
            warningGather.setConfigId(configDo.getId());
            warningGather.setGatherName(configDo.getName());
            warningGather.setStartTime(startTime);
            warningGather.setEndTime(endTime);
            warningGather.setWarnPersonCount(personIds.size());
            warningGather.setGatherAddress(mostFrequentAddress);
            warningGather.setIsRead(0);
            warningGatherMapper.insert(warningGather);

            currentGatherId = warningGather.getId();
            gather.setId(currentGatherId); // 回写ID到聚集对象
            log.debug("创建新聚集记录：ID={}，包含{}人，地址={}", currentGatherId, personIds.size(), mostFrequentAddress);
        }
        // 旧聚集（有ID）：更新主表
        else {
            WarningGatherDO warningGather = warningGatherMapper.selectById(gather.getId());
            if (warningGather == null) {
                log.error("更新聚集失败：聚集ID={}不存在", gather.getId());
                throw new IllegalArgumentException("聚集不存在，ID：" + gather.getId());
            }

            // 更新字段：结束时间、人员数量、配置信息（若有）、地址
            warningGather.setEndTime(endTime);
            warningGather.setWarnPersonCount(personIds.size());
            if (configDo != null) {
                warningGather.setConfigId(configDo.getId());
                warningGather.setGatherName(configDo.getName());
            }
            warningGather.setGatherAddress(mostFrequentAddress);
            warningGatherMapper.updateById(warningGather);

            currentGatherId = gather.getId();
            log.debug("更新聚集记录：ID={}，包含{}人，地址={}", currentGatherId, personIds.size(), mostFrequentAddress);
        }
        return currentGatherId;
    }

    /**
     * 保存聚集人员（保留原有阅读标记）
     */
    private void saveGatherPersons(Long gatherId, List<LocationPointVO> points, String configId) {
        // 1. 缓存旧聚集的阅读状态（更新场景下）
        Map<Long, Integer> personReadStatusMap;
        if (gatherId != null) {
            List<WarningGatherPersonDO> oldPersons = warningGatherPersonMapper.selectList(
                    new LambdaQueryWrapper<WarningGatherPersonDO>()
                            .eq(WarningGatherPersonDO::getGatherId, gatherId)
            );
            // 构建 personId -> isRead 映射
            personReadStatusMap = oldPersons.stream()
                    .collect(Collectors.toMap(
                            WarningGatherPersonDO::getPersonId,
                            WarningGatherPersonDO::getIsRead,
                            (existing, replacement) -> existing // 处理重复personId（理论无）
                    ));
            log.debug("缓存聚集ID={}的阅读状态，共{}条记录", gatherId, personReadStatusMap.size());

            // 删除旧人员记录（先删后插，保证数据一致性）
            warningGatherPersonMapper.deleteByGatherId(gatherId);
        } else {
            personReadStatusMap = new HashMap<>();
        }

        // 2. 按personId分组，同一人只保留最新时间的记录
        List<LocationPointVO> uniquePoints = points.stream()
                .collect(Collectors.toMap(
                        LocationPointVO::getPersonId,  // 按人员ID分组
                        Function.identity(),
                        (existing, replacement) -> {   // 遇到重复人员时保留时间最新的记录
                            try {
                                LocalDateTime existingTime = LocalDateTime.parse(
                                        existing.getLocateTime(), DATE_TIME_FORMATTER);
                                LocalDateTime replacementTime = LocalDateTime.parse(
                                        replacement.getLocateTime(), DATE_TIME_FORMATTER);
                                return replacementTime.isAfter(existingTime) ? replacement : existing;
                            } catch (DateTimeParseException e) {
                                log.error("时间格式解析错误，使用现有记录: {}", e.getMessage());
                                return existing; // 解析失败时保留原有记录
                            }
                        }
                ))
                .values()
                .stream()
                .toList();

        log.debug("聚集ID={}：去重前{}条记录，去重后{}条记录",
                gatherId, points.size(), uniquePoints.size());

        // 3. 构建新人员记录列表
        List<WarningGatherPersonDO> personDOs = uniquePoints.stream()
                .map(point -> {
                    Long personId = point.getPersonId();
                    // 优先用缓存的阅读状态，新人员默认未读（0）
                    Integer isRead = personReadStatusMap.getOrDefault(personId, 0);

                    return WarningGatherPersonDO.builder()
                            .gatherId(gatherId)
                            .configId(configId)
                            .personId(personId)
                            .lng(point.getLng())
                            .lat(point.getLat())
                            .address(point.getAddress())
                            .locationTime(point.getLocateTime())
                            .isRead(isRead) // 恢复/设置阅读状态
                            .build();
                })
                .toList();

        // 4. 批量插入（循环插入兼容低版本MyBatis，高版本可改用insertBatch）
        if (!personDOs.isEmpty()) {
            for (WarningGatherPersonDO personDO : personDOs) {
                try {
                    // 双重检查是否已存在（理论上去重后不会重复，仅作安全校验）
                    LambdaQueryWrapper<WarningGatherPersonDO> queryWrapper = new LambdaQueryWrapper<>();
                    queryWrapper.eq(WarningGatherPersonDO::getGatherId, gatherId)
                            .eq(WarningGatherPersonDO::getPersonId, personDO.getPersonId());

                    if (warningGatherPersonMapper.selectCount(queryWrapper) == 0) {
                        warningGatherPersonMapper.insert(personDO);
                    } else {
                        log.warn("聚集ID={}的人员ID={}已存在，跳过插入",
                                gatherId, personDO.getPersonId());
                    }
                } catch (Exception e) {
                    log.error("保存聚集人员失败：personId={}, gatherId={}",
                            personDO.getPersonId(), personDO.getGatherId(), e);
                    throw new RuntimeException("保存聚集人员异常", e); // 抛异常触发事务回滚
                }
            }
            log.debug("聚集ID={}保存人员记录：共{}条（已保留阅读状态）", gatherId, personDOs.size());
        }
    }

    /**
     * 清理被当前聚集涵盖的旧聚集（避免重复数据）
     */
    private void cleanupCoveredOldGathers(Long currentGatherId, String configId,
                                          LocalDateTime currentStart, LocalDateTime currentEnd,
                                          List<LocationPointVO> currentPoints) {
        // 1. 构建当前聚集人员的唯一标识（configId+personId+locationTime）
        Set<String> currentPersonIdentifiers = currentPoints.stream()
                .map(point -> buildPersonIdentifier(configId, point.getPersonId(), point.getLocateTime()))
                .collect(Collectors.toSet());

        // 2. 查询同一配置下的其他聚集（排除当前聚集）
        LambdaQueryWrapper<WarningGatherDO> query = new LambdaQueryWrapper<>();
        query.eq(WarningGatherDO::getConfigId, configId)
                .ne(WarningGatherDO::getId, currentGatherId);
        List<WarningGatherDO> oldGathers = warningGatherMapper.selectList(query);

        if (oldGathers.isEmpty()) {
            log.debug("聚集ID={}：无同一配置下的旧聚集，无需清理", currentGatherId);
            return;
        }

        // 3. 检查旧聚集是否被当前聚集完全涵盖
        for (WarningGatherDO oldGather : oldGathers) {
            // 条件1：时间范围完全包含（旧开始>=当前开始，旧结束<=当前结束）
            boolean timeCovered = !oldGather.getStartTime().isBefore(currentStart)
                    && !oldGather.getEndTime().isAfter(currentEnd);
            if (!timeCovered) {
                log.debug("旧聚集ID={}：时间范围不被包含，跳过清理", oldGather.getId());
                continue;
            }

            // 条件2：人员记录完全包含（旧人员的唯一标识都在当前聚集内）
            List<WarningGatherPersonDO> oldPersons = warningGatherPersonMapper.selectList(
                    new LambdaQueryWrapper<WarningGatherPersonDO>()
                            .eq(WarningGatherPersonDO::getGatherId, oldGather.getId())
            );
            boolean allPersonsCovered = oldPersons.stream()
                    .map(person -> buildPersonIdentifier(person.getConfigId(),
                            person.getPersonId(), person.getLocationTime()))
                    .allMatch(currentPersonIdentifiers::contains);

            // 满足所有条件：删除旧聚集及其人员记录
            if (allPersonsCovered) {
                warningGatherPersonMapper.deleteByGatherId(oldGather.getId());
                warningGatherMapper.deleteById(oldGather.getId());
                log.debug("旧聚集ID={}：被当前聚集ID={}完全涵盖，已删除", oldGather.getId(), currentGatherId);
            } else {
                log.debug("旧聚集ID={}：人员不完全包含，跳过清理", oldGather.getId());
            }
        }
    }

    /**
     * 辅助：统计最频繁的地址（作为聚集地址）
     */
    private String getMostFrequentAddress(List<LocationPointVO> points) {
        return points.stream()
                .filter(point -> point.getAddress() != null && !point.getAddress().trim().isEmpty())
                .collect(Collectors.groupingBy(LocationPointVO::getAddress, Collectors.counting()))
                .entrySet().stream()
                .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
                .map(Map.Entry::getKey)
                .findFirst()
                .orElse(points.getFirst().getAddress()); // 无有效地址时用第一个点的地址
    }

    /**
     * 辅助：构建人员唯一标识（避免重复判断）
     */
    private String buildPersonIdentifier(String configId, Long personId, String locationTime) {
        return configId + "_" + personId + "_" + locationTime;
    }
}